﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CellController : MonoBehaviour {
	// 单例模式
	private static CellController m_Instance = null;
	public static CellController Instance {
		get {
			if(m_Instance == null) {
				m_Instance = GameObject.FindObjectOfType(typeof(CellController)) as CellController;
			}

			if (!m_Instance) {
				GameObject obj = new GameObject("CellController");
				m_Instance = obj.AddComponent<CellController>();
			}
			return m_Instance;
		}
	}

	//场景的宽和高
	private float width;
	private float height;

	//半径暂时统一
	private float radius;

	//最近重叠大小(半径的倍数, 为0表示一个细胞的原点能在另一个细胞的圆边上，而不是完全重合）
	float minEpsilon = 0.5f;

	//最远偏移距离
	float maxEpsilon = 1f;

	//两个组织之间的最小距离，此为倍数（即由稀疏程度算出来的半径倍数）
	float minOrgDist = 0.5f;
	//两个组织之间的最小距离，此为倍数（即由稀疏程度算出来的半径倍数）
	float maxOrgDist = 6;
	//组织细胞个数的抖动大小，为0则各个组织的细胞数趋于平均，越大则越不平均,最大为1
	float numShakeRate = 0.5f;

	//细胞组织的稀疏程度（最好在0～1之间，其实如果设置得太大也不会有变化了）
	float density = 1f;

	//病毒细胞与组织细胞半径的最小距离（如果设置为0则可能会发生小部分重叠情况）
	float virusToCellEpsilon = 1f;

	public List<Vector2> organizes = new List<Vector2>();
	List<Vector2> viruses = new List<Vector2> ();
	List<GameObject> organizeList = new List<GameObject> ();

	//估计的半径
	private float evaluateRadius = 0;
	public GameObject cellPrefab;
	public GameObject virusPrefab;
	public GameObject infectedCellPrefab;
	public GameObject TCellPrefab;
	public GameObject BCellPrefab;
	public GameObject SCellPrefab;

	protected ContactFilter2D contactFilter;
	protected Collider2D[] hitBuffer = new Collider2D[16];

	private void Awake() {
		width = GameController.Instance.SceneWidth;
		height = GameController.Instance.SceneHeight;

		contactFilter.useTriggers = false;
		radius = cellPrefab.GetComponent<CircleCollider2D> ().radius;
		if (density < 0) {
			density = 0;
		} else if (density > 1) {
			density = 1;
		}
		evaluateRadius = ((9 - 5) * density + 5) * radius;
	}

	// void Start() {
		
	// 	// //经检验，5是最好的选择
	// 	// generateOrganizations(400, 7, 9);
	// }

	/**
	 * 生成totalNum个细胞，组织个数为minOrgNum至maxOrgNum之间
	 */ 
	public void generateOrganizations(int totalNum, int minOrgNum, int maxOrgNum) {
		int groupNum = Random.Range (minOrgNum, maxOrgNum + 1);
		//每个组的平均细胞数
		int averageNum = totalNum / groupNum;
		//抖动的范围
		int shakeNum = (int)(averageNum * numShakeRate);
		List<int> numList = new List<int> ();
		//先把区间写好,五个组则有六个区间坐标
		for (int i = 0; i < groupNum + 1; i++) {
			numList.Add (i * averageNum);
		}
		numList [groupNum] = totalNum;
		//然后把区间里的各个坐标打乱，即向左右方向抖动， 只需要抖动中间四个坐标
		for (int i = 1; i < groupNum; i++) {
			int newAxis = Random.Range (numList[i] - shakeNum, numList[i] + shakeNum + 1);
			numList [i] = newAxis;
		}
		//边界坐标，组织的原点只能在边界坐标之内，否则会超出屏幕范围
		float boarderWidth = width / 2 - evaluateRadius - cellPrefab.GetComponent<CircleCollider2D>().radius;
		float boarderHeight = height / 2 - evaluateRadius - cellPrefab.GetComponent<CircleCollider2D>().radius;
		List<Vector2> originPosList = new List<Vector2> ();
		int tryNum = 0;
		int maxTryNum = 100;
		// 随机一个初始组织位置
		float posX = Random.value * boarderWidth * 2 - boarderWidth;
		float posY = Random.value * boarderHeight * 2 - boarderHeight;
		Vector2 orgPos = new Vector2 (posX, posY);
		originPosList.Add (orgPos);
		//两个组织之间的最小距离
		while(originPosList.Count < groupNum) {
			tryNum++;
			float disRate = Random.value * (maxOrgDist - minOrgDist) + minOrgDist;
			float dis = evaluateRadius * 2 + evaluateRadius * disRate;
			Vector2 theOrg = originPosList[Random.Range(0, originPosList.Count)];
			int searchNum = 0;
			bool available = true;
			float angle = Random.value * Mathf.PI * 2;
			do {
				available = true;
				searchNum++;
				angle += Mathf.PI / 2;
				Vector2 newPos = new Vector2 (theOrg.x + dis * Mathf.Cos (angle), theOrg.y + dis * Mathf.Sin (angle));
				//不能超出圆外
				if (Mathf.Abs(newPos.x) > boarderWidth || Mathf.Abs(newPos.y) > boarderHeight) {
					available = false;
					continue;
				}
				if (!canPutOrganization(newPos, cellPrefab.GetComponent<CircleCollider2D>().radius)) {
					available = false;
					continue;
				}
				foreach (Vector2 org in originPosList) {
					//如果与一个细胞的距离小于其半径，则判断该位置不许存在细胞
					if (org != theOrg && Vector2.Distance (newPos, org) < dis) {
						available = false;
						break;
					}
				}
			} while(!available && searchNum < 4);
			if (!available) {
				if (tryNum > maxTryNum) {
					Debug.Log ("已几乎找不到任何能生成组织的地方");
					break;
				}
				continue;
			} else {
				Vector2 newPos = new Vector2 (theOrg.x + dis * Mathf.Cos (angle), theOrg.y + dis * Mathf.Sin (angle));
				originPosList.Add (newPos);
				tryNum = 0;
			}
		}
		for (int i = 0; i < originPosList.Count; i++) {
			Vector2 _orgPos = originPosList [i];
			GameObject orgNode = new GameObject (string.Format("Orgnize{0}", i));
			orgNode.AddComponent<OrgnizeController> ();
			organizeList.Add (orgNode);
			generateCells (orgNode, numList [i + 1] - numList [i], _orgPos);
			organizes.Add (_orgPos);
		}
	}

	/**
	 * 在一个点以一定的半径生成一个组织（如果半径太小可能组织里的细胞会小于minCellNum
	 * @param cellNum 该组织的细胞个数
	 * @param pos 组织的原点坐标
	 */
	public List<Vector4> generateCells(GameObject parent, int cellNum, Vector2 pos) {
		//用3D向量，前两维表示坐标，第三维表示半径（暂时固定所以都一样），第四维表示周围的细胞数
		List<Vector4> cellList = new List<Vector4> ();
		//原点,后面生成的细胞都是以其为原点
		Vector4 originPos = new Vector4 (pos.x, pos.y, radius, 0);
		Vector4 leftCell = originPos, rightCell = originPos, topCell = originPos, downCell = originPos;
		parent.transform.position = new Vector2 (originPos.x, originPos.y);
		cellList.Add (originPos);
		//是否重合
		bool overlap = true;
		//尝试次数，如果给定的半径太小，则可能陷入死循环,所以加此变量判定在超过一定的循环次数无果后则直接退出循环并完成该细胞组的绘制
		int tryNum = 0;
		int maxTryNum = 100;
		while (cellList.Count < cellNum) {
			tryNum++;
			//从细胞组中随机选择一个细胞
			Vector4 theCell = getAnCell(cellList);
			float srcRadius = theCell.z;
			//新生成的细胞的半径，暂定一样
			float newRadius = radius;
			float dis = srcRadius;
			if (overlap) {
				dis += Random.value * newRadius + minEpsilon * srcRadius;
			} else {
				dis += Random.value * maxEpsilon * srcRadius + newRadius;
			}
			float angle = Random.value * Mathf.PI * 2;
			//搜寻次数，如果多于四次则退出循环重新找点
			int searchNum = 0;
			bool available = true;
			do {
				available = true;
				searchNum++;
				angle += Mathf.PI / 2;
				Vector4 newPos = new Vector4 (theCell.x + dis * Mathf.Cos (angle), theCell.y + dis * Mathf.Sin (angle), radius, 0);
				if (!canPutOrganization(new Vector2(newPos.x, newPos.y), cellPrefab.GetComponent<CircleCollider2D>().radius)) {
					available = false;
					continue;
				}
				//不能超出圆外
				float distToOrigin = computeDist(newPos, originPos);
				if (distToOrigin > evaluateRadius) {
					available = false;
					continue;
				}
				foreach (Vector4 cell in cellList) {
					//如果与一个细胞的距离小于其半径，则判断该位置不许存在细胞
					if (cell != theCell && computeDist (newPos, cell) < cell.z + minEpsilon * newPos.z) {
						available = false;
						break;
					}
				}
			} while(!available && searchNum < 4);
			//如果周围几乎都有细胞，则重新寻找下一个
			if (!available) {
				if (tryNum > maxTryNum) {
					Debug.Log ("已几乎找不到任何能生成细胞的地方");
					break;
				}
				continue;
			} else {
				Vector4 newPos = new Vector4 (theCell.x + dis * Mathf.Cos (angle), theCell.y + dis * Mathf.Sin (angle), radius, 0);
				cellList.Add (newPos);
				if (newPos.x < leftCell.x) {
					leftCell = newPos;
				}
				if (newPos.x > rightCell.x) {
					rightCell = newPos;
				}
				if (newPos.y < downCell.y) {
					downCell = newPos;
				}
				if (newPos.y > topCell.y) {
					topCell = newPos;
				}
				overlap = !overlap;
				theCell = new Vector4 (theCell.x, theCell.y, theCell.z, theCell.w + 1);
				//theCell.w++;
				for (int i = 0; i < cellList.Count; i++) {
					Vector4 cell = cellList[i];
					//在其范围内，则对应的w自增1
					if (cell != theCell && computeDist (newPos, cell) >= cell.z && computeDist (newPos, cell) <= cell.z + newPos.z + maxEpsilon * newPos.z) {
						cell = new Vector4 (cell.x, cell.y, cell.z, cell.w + 1);
//						cell.w++;
					}
				}
				tryNum = 0;
			}
		}
		foreach (Vector4 cell in cellList) {
			GameObject cellObject = Instantiate (cellPrefab);
			cellObject.transform.position = new Vector2 (cell.x, cell.y);
			cellObject.transform.parent = parent.transform;
			if (cell == leftCell) {
				parent.GetComponent<OrgnizeController> ().leftCell = cellObject;
			}
			if (cell == rightCell) {
				parent.GetComponent<OrgnizeController> ().rightCell = cellObject;
			}
			if (cell == topCell) {
				parent.GetComponent<OrgnizeController> ().topCell = cellObject;
			}
			if (cell == downCell) {
				parent.GetComponent<OrgnizeController> ().downCell = cellObject;
			}
		}
		return cellList;
	}

	public void generateVirus(int num) {
		float boarderWidth = width / 2 - virusPrefab.GetComponent<CircleCollider2D>().radius;
		float boarderHeight = height / 2 - virusPrefab.GetComponent<CircleCollider2D>().radius;
		List<Vector2> virusList = new List<Vector2> ();
		while(virusList.Count < num) {
			int tryNum = 0;
			int maxTryNum = 100;
			// 随机一个初始组织位置
			float posX = 0;
			float posY = 0;
			bool available = true;
			do {
				tryNum++;
				available = true;
				posX = Random.value * boarderWidth * 2 - boarderWidth;
				posY = Random.value * boarderHeight * 2 - boarderHeight;
				Vector2 virusPos = new Vector2(posX, posY);
				if (organizes.Count > 0) {
					foreach (Vector2 organizePos in organizes) {
						if (Vector2.Distance(organizePos, virusPos) <= evaluateRadius + virusPrefab.GetComponent<CircleCollider2D>().radius * (1 + virusToCellEpsilon)) {
							available = false;
							break;
						}
					}
				}
				if (!canPutOrganization(virusPos, virusPrefab.GetComponent<CircleCollider2D>().radius)) {
					available = false;
					continue;
				}
				if  (available && virusList.Count > 0) {
					foreach (Vector2 addedVirusPos in virusList) {
						if (Vector2.Distance(addedVirusPos, virusPos) <= 2 * virusPrefab.GetComponent<CircleCollider2D>().radius) {
							available = false;
							break;
						}
					}
				}
			} while(!available && tryNum < maxTryNum);
			if (!available) {
				Debug.Log ("已几乎找不到任何能生成病毒的地方");
				break;
			} else {
				Vector2 virusPos = new Vector2(posX, posY);
				virusList.Add (virusPos);
				tryNum = 0;
			}
		}
		for (int i = 0; i < virusList.Count; i++) {
			Vector2 _virusPos = virusList [i];
			GameObject virusObject = Instantiate (virusPrefab);
			virusObject.transform.position = _virusPos;
		}
		viruses = virusList;
	}

	private bool canPutOrganization(Vector2 pos, float radius) {
		int count = Physics2D.OverlapCircle (pos, radius, contactFilter, hitBuffer);
		bool can = true;
		if (count > 0) {
			for (int i = 0; i < count; i++) {
				Collider2D collider = hitBuffer [i];
				if (collider.tag == "obstacle") {
					can = false;
					break;
				}
			}
		}
		return can;
	}

	public void infectCells(int num) {
		List<GameObject> infectCellList = new List<GameObject> ();
		int tryNum = 0;
		int tryMaxNum = 100;
		while (infectCellList.Count < num) {
			tryNum++;
			int organIdex = Random.Range (0, organizeList.Count);
			GameObject organ = organizeList [organIdex];
			int cellIndex = Random.Range (0, organ.transform.childCount);
			GameObject cell = organ.transform.GetChild (cellIndex).gameObject;
			if (!infectCellList.Contains (cell)) {
				infectCellList.Add (cell);
			} else {
				if (tryNum > tryMaxNum) {
					Debug.Log ("已几乎找不到任何能变异的细胞");
					break;
				}
			}
			Debug.Log(string.Format("infect num:{0}", infectCellList.Count));
		}
		foreach (GameObject cell in infectCellList) {
			GameObject infectedCell = Instantiate (infectedCellPrefab);
			infectedCell.transform.parent = cell.transform.parent;
			infectedCell.transform.position = cell.transform.position;
			Destroy (cell);
		}
	}

	public void generateCells(int TNum, int BNum, int SNum) {
		float TBoarderWidth = width / 2 - TCellPrefab.GetComponent<CircleCollider2D>().radius;
		float TBoarderHeight = height / 2 - TCellPrefab.GetComponent<CircleCollider2D>().radius;
		float BBoarderWidth = width / 2 - BCellPrefab.GetComponent<CircleCollider2D>().radius;
		float BBoarderHeight = height / 2 - BCellPrefab.GetComponent<CircleCollider2D>().radius;
		float SBoarderWidth = width / 2 - SCellPrefab.GetComponent<CircleCollider2D>().radius;
		float SBoarderHeight = height / 2 - SCellPrefab.GetComponent<CircleCollider2D>().radius;
		List<Vector2> cellList = new List<Vector2> ();
		while(cellList.Count < TNum + BNum + SNum) {
			int tryNum = 0;
			int maxTryNum = 100;
			float boarderWidth = 0f, boarderHeight = 0f;
			float theRadius = 0;
			if (cellList.Count < TNum) {
				boarderWidth = TBoarderWidth;
				boarderHeight = TBoarderHeight;
				theRadius = TCellPrefab.GetComponent<CircleCollider2D> ().radius;
			} else if (cellList.Count >= TNum && cellList.Count < TNum + BNum) {
				boarderWidth = BBoarderWidth;
				boarderHeight = BBoarderHeight;
				theRadius = BCellPrefab.GetComponent<CircleCollider2D> ().radius;
			} else {
				boarderWidth = SBoarderWidth;
				boarderHeight = SBoarderHeight;
				theRadius = SCellPrefab.GetComponent<CircleCollider2D> ().radius;
			}
			// 随机一个初始组织位置
			float posX = 0;
			float posY = 0;
			bool available = true;
			do {
				tryNum++;
				available = true;
				posX = Random.value * boarderWidth * 2 - boarderWidth;
				posY = Random.value * boarderHeight * 2 - boarderHeight;
				Vector2 cellPos = new Vector2(posX, posY);
				if (organizes.Count > 0) {
					foreach (Vector2 organizePos in organizes) {
						if (Vector2.Distance(organizePos, cellPos) <= evaluateRadius + theRadius) {
							available = false;
							break;
						}
					}
				}
				if (viruses.Count > 0) {
					foreach (Vector2 virus in viruses) {
						if (Vector2.Distance(virus, cellPos) <= evaluateRadius + theRadius) {
							available = false;
							break;
						}
					}
				}
				if (!canPutOrganization(cellPos, virusPrefab.GetComponent<CircleCollider2D>().radius)) {
					available = false;
					continue;
				}
				if  (available && cellList.Count > 0) {
					foreach (Vector2 addedVirusPos in cellList) {
						if (Vector2.Distance(addedVirusPos, cellPos) <= 2 * theRadius) {
							available = false;
							break;
						}
					}
				}
			} while(!available && tryNum < maxTryNum);
			if (!available) {
				Debug.Log ("已几乎找不到任何能生成免疫细胞的地方");
				break;
			} else {
				Vector2 cellPos = new Vector2(posX, posY);
				cellList.Add (cellPos);
				tryNum = 0;
			}
		}
		for (int i = 0; i < cellList.Count; i++) {
			Vector2 _cellPos = cellList [i];
			GameObject cellObject;
			if (i < TNum) {
				cellObject = Instantiate (TCellPrefab);
			} else if (i >= TNum && i < TNum + BNum) {
				cellObject = Instantiate (BCellPrefab);
			} else {
				cellObject = Instantiate (SCellPrefab);
			}
			cellObject.transform.position = _cellPos;
		}
	}

	//在细胞列表中获取一个细胞
	private Vector4 getAnCell(List<Vector4> cellList) {
		Vector4 theCell;
		do {
			theCell = cellList [Random.Range (0, cellList.Count)];
		} while(theCell.w >= 4);
//		if (theCell) {
//			Debug.LogError("get cell error!");
//			return cellList [Random.Range (0, cellList.Count)];
//		}
		return theCell;
	}

	//计算两点之间的距离
	public float computeDist(Vector4 pos1, Vector4 pos2) {
		Vector2 _pos1 = new Vector2 (pos1.x, pos1.y);
		Vector2 _pos2 = new Vector2 (pos2.x, pos2.y);
		return Vector2.Distance (_pos1, _pos2);
	}

	
}
